Merge "Change some line comments to multi line comments"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Wed, 14 Oct 2015 08:27:43 +0000 (08:27 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Wed, 14 Oct 2015 08:27:43 +0000 (08:27 +0000)
57 files changed:
RELEASE-NOTES-1.26
RELEASE-NOTES-1.27
composer.json
includes/Html.php
includes/HtmlFormatter.php
includes/Linker.php
includes/MediaWiki.php
includes/Title.php
includes/User.php
includes/libs/IEUrlExtension.php
includes/password/PasswordFactory.php
includes/specials/SpecialChangePassword.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUserlogin.php
includes/upload/UploadBase.php
includes/upload/UploadStash.php
languages/classes/LanguageHy.php
languages/classes/LanguageWa.php
languages/i18n/en.json
languages/i18n/qqq.json
maintenance/backupTextPass.inc
maintenance/cleanupCaps.php
maintenance/cleanupSpam.php
maintenance/cleanupTable.inc
maintenance/deleteBatch.php
maintenance/deleteEqualMessages.php
maintenance/edit.php
maintenance/importImages.php
maintenance/importSiteScripts.php
maintenance/moveBatch.php
maintenance/protect.php
maintenance/rollbackEdits.php
maintenance/tables.sql
maintenance/undelete.php
resources/Resources.php
resources/lib/oojs-ui/i18n/nb.json
resources/lib/oojs-ui/i18n/pa.json
resources/lib/oojs-ui/oojs-ui-apex-noimages.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui.js
resources/src/mediawiki.widgets/mw.widgets.TitleSearchWidget.js
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/includes/BlockTest.php
tests/phpunit/includes/LinkFilterTest.php
tests/phpunit/includes/TestUser.php
tests/phpunit/includes/UserTest.php
tests/phpunit/includes/api/ApiBlockTest.php
tests/phpunit/includes/api/ApiCreateAccountTest.php
tests/phpunit/includes/api/UserWrapper.php
tests/phpunit/includes/cache/GenderCacheTest.php
tests/phpunit/includes/password/PasswordTest.php
tests/phpunit/maintenance/DumpTestCase.php
tests/phpunit/maintenance/MaintenanceTest.php
tests/phpunit/maintenance/backupTextPassTest.php

index efca318..53d9188 100644 (file)
@@ -94,6 +94,12 @@ production.
 * If search returns zero results and current search engine has a "did you mean"
   suggestion, results for suggestion will be shown. Can be disabled by setting
   $wgSearchRunSuggestedQuery to false.
+* Added several JavaScript libraries for uploading files to MediaWiki
+  from the client-side. See documentation for mw.Upload and its
+  subclasses for more information.
+* Added OOUI dialogs and layout for file upload interfaces. See
+  documentation for mw.Upload.Dialog, mw.Upload.BookletLayout and its
+  subclasses for more information.
 
 == External libraries ==
 === Upgraded external libraries ===
index 697210c..b34b3c0 100644 (file)
@@ -16,7 +16,6 @@ production.
   1000 for the latter) are now hard-coded.
 * $wgDebugDumpSqlLength was removed (deprecated in 1.24).
 * $wgDebugDBTransactions was removed (deprecated in 1.20).
-* $wgRemoteUploadTarget (added in 1.26) removed, replaced by $wgForeignUploadTargets
 * $wgUseXVO has been removed, as it provides functionality only used by
   custom Wikimedia patches against Squid 2.x that probably noone uses in
   production anymore. There is now $wgUseKeyHeader that provides similar
@@ -25,6 +24,25 @@ production.
 * $wgScriptExtension (and support for '.php5' entry points) was removed. See the
   deprecation notice in the release notes for version 1.25 for advice on how to
   preserve support for '.php5' entry points via URL rewriting.
+* Password handling via the User object has been deprecated and partially
+  removed, pending the future introduction of AuthManager. In particular:
+** expirePassword(), getPasswordExpireDate(), resetPasswordExpiration(), and
+   getPasswordExpired() have been removed. They were unused outside of core.
+** The mPassword, mNewpassword, mNewpassTime, and mPasswordExpires fields are
+   now private and will be removed in the future.
+** The getPassword() and getTemporaryPassword() methods now throw
+   BadMethodCallException and will be removed in the future.
+** The ability to pass 'password' and 'newpassword' to createNew() has been
+   removed. The only users of it seem to have been using it to set invalid
+   passwords, and so shouldn't be greatly affected.
+** setPassword(), setInternalPassword(), and setNewpassword() have been
+   deprecated, pending the introduction of AuthManager.
+** User::randomPassword() is deprecated in favor of a new method
+   PasswordFactory::generateRandomPasswordString()
+** User::getPasswordFactory() is deprecated, callers should just create a
+   PasswordFactory themselves.
+** A new constructor, User::newSystemUser(), has been added to simplify the
+   creation of passwordless "system" users for logged actions.
 
 === New features in 1.27 ===
 * $wgDataCenterId and $wgDataCenterRoles where added, which will serve as
@@ -41,12 +59,8 @@ production.
 * $wgCdnMaxageLagged was added, which limits the CDN cache TTL
   when any load balancer uses a DB that is lagged beyond the 'max lag'
   setting in the relevant section of $wgLBFactoryConf.
-* Added several JavaScript libraries for uploading files to MediaWiki
-  from the client-side. See documentation for mw.Upload and its
-  subclasses for more information.
-* Added OOUI dialogs and layout for file upload interfaces. See
-  documentation for mw.Upload.Dialog, mw.Upload.BookletLayout and its
-  subclasses for more information.
+* User::newSystemUser() may be used to simplify the creation of passwordless
+  "system" users for logged actions from scripts and extensions.
 
 ==== External libraries ====
 
index 46ff59c..6209017 100644 (file)
@@ -21,7 +21,7 @@
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.16",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.12.11",
+               "oojs/oojs-ui": "0.12.12",
                "oyejorge/less.php": "1.7.0.9",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
index c61dca8..506b9cc 100644 (file)
@@ -597,7 +597,6 @@ class Html {
                                // we may as well not call htmlspecialchars().
                                // @todo FIXME: Verify that we actually need to
                                // escape \n\r\t here, and explain why, exactly.
-                               #
                                // We could call Sanitizer::encodeAttribute() for this, but we
                                // don't because we're stubborn and like our marginal savings on
                                // byte size from not having to encode unnecessary quotes.
index 83db268..2d81b28 100644 (file)
@@ -74,10 +74,8 @@ class HtmlFormatter {
                        }
 
                        // Workaround for bug that caused spaces before references
-                       // to disappear during processing:
-                       // https://phabricator.wikimedia.org/T55086
-                       //
-                       // Please replace with a better fix if one can be found.
+                       // to disappear during processing: https://phabricator.wikimedia.org/T55086
+                       // TODO: Please replace with a better fix if one can be found.
                        $html = str_replace( ' <', '&#32;<', $html );
 
                        libxml_use_internal_errors( true );
@@ -295,7 +293,6 @@ class HtmlFormatter {
                        $html = $this->fixLibXml( $html );
                        if ( wfIsWindows() ) {
                                // Cleanup for CRLF misprocessing of unknown origin on Windows.
-                               //
                                // If this error continues in the future, please track it down in the
                                // XML code paths if possible and fix there.
                                $html = str_replace( '&#13;', '', $html );
index 2e33bd1..e821004 100644 (file)
@@ -623,7 +623,6 @@ class Linker {
                        # Create a thumbnail. Alignment depends on the writing direction of
                        # the page content language (right-aligned for LTR languages,
                        # left-aligned for RTL languages)
-                       #
                        # If a thumbnail width has not been provided, it is set
                        # to the default user option as specified in Language*.php
                        if ( $fp['align'] == '' ) {
index aee6ee1..3399790 100644 (file)
@@ -203,10 +203,9 @@ class MediaWiki {
                        // curid and oldid request  parameters would allow page titles to be enumerated even
                        // when they are not guessable. So we reset the title to Special:Badtitle before the
                        // permissions error is displayed.
-                       //
+
                        // The skin mostly uses $this->context->getTitle() these days, but some extensions
                        // still use $wgTitle.
-
                        $badTitle = SpecialPage::getTitleFor( 'Badtitle' );
                        $this->context->setTitle( $badTitle );
                        $wgTitle = $badTitle;
@@ -628,7 +627,7 @@ class MediaWiki {
                                        // redirecting to HTTPS. It's likely such a request is going
                                        // to fail due to post data being lost, but let's try anyway
                                        // and just log the instance.
-                                       //
+
                                        // @todo FIXME: See if we could issue a 307 or 308 here, need
                                        // to see how clients (automated & browser) behave when we do
                                        wfDebugLog( 'RedirectedPosts', "Redirected from HTTP to HTTPS: $oldUrl" );
index 8e5fae9..7fb2b06 100644 (file)
@@ -1008,10 +1008,8 @@ class Title {
         */
        public function getNsText() {
                if ( $this->isExternal() ) {
-                       // This probably shouldn't even happen. ohh man, oh yuck.
-                       // But for interwiki transclusion it sometimes does.
-                       // Shit. Shit shit shit.
-                       //
+                       // This probably shouldn't even happen,
+                       // but for interwiki transclusion it sometimes does.
                        // Use the canonical namespaces if possible to try to
                        // resolve a foreign namespace.
                        if ( MWNamespace::exists( $this->mNamespace ) ) {
index 75649a7..0dfdfc4 100644 (file)
@@ -28,7 +28,7 @@ define( 'EDIT_TOKEN_SUFFIX', '+\\' );
 
 /**
  * The User object encapsulates all of the user-specific settings (user_id,
- * name, rights, password, email address, options, last login time). Client
+ * name, rights, email address, options, last login time). Client
  * classes use the getXXX() functions to access these fields. These functions
  * do all the work of determining whether the user is logged in,
  * whether the requested option can be satisfied from cookies or
@@ -64,11 +64,6 @@ class User implements IDBAccessObject {
         */
        const GETOPTIONS_EXCLUDE_DEFAULTS = 1;
 
-       /**
-        * @var PasswordFactory Lazily loaded factory object for passwords
-        */
-       private static $mPasswordFactory = null;
-
        /**
         * Array of Strings List of member variables which are saved to the
         * shared cache (memcached). Any operation which changes the
@@ -190,20 +185,22 @@ class User implements IDBAccessObject {
        public $mName;
        /** @var string */
        public $mRealName;
+
        /**
-        * @todo Make this actually private
-        * @private
-        * @var Password
-        */
-       public $mPassword;
-       /**
-        * @todo Make this actually private
-        * @private
-        * @var Password
+        * These fields were marked "@private", but were defined as public to
+        * maintain compatibility with PHP4 code since PHP4 didn't support access
+        * restrictions. AuthManager makes password handling pluggable, meaning
+        * these fields don't make sense anymore. If this broke something, see
+        * T89459 for the context of the change.
+        * @deprecated These are mostly unused, but kept for now to raise errors on attempted access.
         */
-       public $mNewpassword;
-       /** @var string */
-       public $mNewpassTime;
+       // @{
+       private $mPassword = null;
+       private $mNewpassword;
+       private $mNewpassTime;
+       private $mPasswordExpires;
+       // @}
+
        /** @var string */
        public $mEmail;
        /** @var string TS_MW timestamp from the DB */
@@ -226,8 +223,6 @@ class User implements IDBAccessObject {
        public $mGroups;
        /** @var array */
        protected $mOptionOverrides;
-       /** @var string */
-       protected $mPasswordExpires;
        // @}
 
        /**
@@ -559,7 +554,7 @@ class User implements IDBAccessObject {
         * The row should have the following fields from the user table in it:
         * - either user_name or user_id to load further data if needed (or both)
         * - user_real_name
-        * - all other fields (email, password, etc.)
+        * - all other fields (email, etc.)
         * It is useless to provide the remaining fields if either user_id,
         * user_name and user_real_name are not provided because the whole row
         * will be loaded once more from the database when accessing them.
@@ -574,6 +569,97 @@ class User implements IDBAccessObject {
                return $user;
        }
 
+       /**
+        * Static factory method for creation of a "system" user from username.
+        *
+        * A "system" user is an account that's used to attribute logged actions
+        * taken by MediaWiki itself, as opposed to a bot or human user. Examples
+        * might include the 'Maintenance script' or 'Conversion script' accounts
+        * used by various scripts in the maintenance/ directory or accounts such
+        * as 'MediaWiki message delivery' used by the MassMessage extension.
+        *
+        * This can optionally create the user if it doesn't exist, and "steal" the
+        * account if it does exist.
+        *
+        * @param string $name Username
+        * @param array $options Options are:
+        *  - validate: As for User::getCanonicalName(), default 'valid'
+        *  - create: Whether to create the user if it doesn't already exist, default true
+        *  - steal: Whether to reset the account's password and email if it
+        *    already exists, default false
+        * @return User|null
+        */
+       public static function newSystemUser( $name, $options = array() ) {
+               $options += array(
+                       'validate' => 'valid',
+                       'create' => true,
+                       'steal' => false,
+               );
+
+               $name = self::getCanonicalName( $name, $options['validate'] );
+               if ( $name === false ) {
+                       return null;
+               }
+
+               $dbw = wfGetDB( DB_MASTER );
+               $row = $dbw->selectRow(
+                       'user',
+                       array_merge(
+                               self::selectFields(),
+                               array( 'user_password', 'user_newpassword' )
+                       ),
+                       array( 'user_name' => $name ),
+                       __METHOD__
+               );
+               if ( !$row ) {
+                       // No user. Create it?
+                       return $options['create'] ? self::createNew( $name ) : null;
+               }
+               $user = self::newFromRow( $row );
+
+               // A user is considered to exist as a non-system user if it has a
+               // password set, or a temporary password set, or an email set.
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               try {
+                       $password = $passwordFactory->newFromCiphertext( $row->user_password );
+               } catch ( PasswordError $e ) {
+                       wfDebug( 'Invalid password hash found in database.' );
+                       $password = PasswordFactory::newInvalidPassword();
+               }
+               try {
+                       $newpassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
+               } catch ( PasswordError $e ) {
+                       wfDebug( 'Invalid password hash found in database.' );
+                       $newpassword = PasswordFactory::newInvalidPassword();
+               }
+               if ( !$password instanceof InvalidPassword || !$newpassword instanceof InvalidPassword
+                       || $user->mEmail
+               ) {
+                       // User exists. Steal it?
+                       if ( !$options['steal'] ) {
+                               return null;
+                       }
+
+                       $nopass = PasswordFactory::newInvalidPassword()->toString();
+
+                       $dbw->update(
+                               'user',
+                               array(
+                                       'user_password' => $nopass,
+                                       'user_newpassword' => $nopass,
+                                       'user_newpass_time' => null,
+                               ),
+                               array( 'user_id' => $user->getId() ),
+                               __METHOD__
+                       );
+                       $user->invalidateEmail();
+                       $user->saveSettings();
+               }
+
+               return $user;
+       }
+
        // @}
 
        /**
@@ -875,73 +961,6 @@ class User implements IDBAccessObject {
                }
        }
 
-       /**
-        * Expire a user's password
-        * @since 1.23
-        * @param int $ts Optional timestamp to convert, default 0 for the current time
-        */
-       public function expirePassword( $ts = 0 ) {
-               $this->loadPasswords();
-               $timestamp = wfTimestamp( TS_MW, $ts );
-               $this->mPasswordExpires = $timestamp;
-               $this->saveSettings();
-       }
-
-       /**
-        * Clear the password expiration for a user
-        * @since 1.23
-        * @param bool $load Ensure user object is loaded first
-        */
-       public function resetPasswordExpiration( $load = true ) {
-               global $wgPasswordExpirationDays;
-               if ( $load ) {
-                       $this->load();
-               }
-               $newExpire = null;
-               if ( $wgPasswordExpirationDays ) {
-                       $newExpire = wfTimestamp(
-                               TS_MW,
-                               time() + ( $wgPasswordExpirationDays * 24 * 3600 )
-                       );
-               }
-               // Give extensions a chance to force an expiration
-               Hooks::run( 'ResetPasswordExpiration', array( $this, &$newExpire ) );
-               $this->mPasswordExpires = $newExpire;
-       }
-
-       /**
-        * Check if the user's password is expired.
-        * TODO: Put this and password length into a PasswordPolicy object
-        * @since 1.23
-        * @return string|bool The expiration type, or false if not expired
-        *      hard: A password change is required to login
-        *      soft: Allow login, but encourage password change
-        *      false: Password is not expired
-        */
-       public function getPasswordExpired() {
-               global $wgPasswordExpireGrace;
-               $expired = false;
-               $now = wfTimestamp();
-               $expiration = $this->getPasswordExpireDate();
-               $expUnix = wfTimestamp( TS_UNIX, $expiration );
-               if ( $expiration !== null && $expUnix < $now ) {
-                       $expired = ( $expUnix + $wgPasswordExpireGrace < $now ) ? 'hard' : 'soft';
-               }
-               return $expired;
-       }
-
-       /**
-        * Get this user's password expiration date. Since this may be using
-        * the cached User object, we assume that whatever mechanism is setting
-        * the expiration date is also expiring the User cache.
-        * @since 1.23
-        * @return string|null The datestamp of the expiration, or null if not set
-        */
-       public function getPasswordExpireDate() {
-               $this->load();
-               return $this->mPasswordExpires;
-       }
-
        /**
         * Given unvalidated user input, return a canonical username, or false if
         * the username is invalid.
@@ -1022,19 +1041,12 @@ class User implements IDBAccessObject {
        /**
         * Return a random password.
         *
+        * @deprecated since 1.27, use PasswordFactory::generateRandomPasswordString()
         * @return string New random password
         */
        public static function randomPassword() {
                global $wgMinimalPasswordLength;
-               // Decide the final password length based on our min password length,
-               // stopping at a minimum of 10 chars.
-               $length = max( 10, $wgMinimalPasswordLength );
-               // Multiply by 1.25 to get the number of hex characters we need
-               $length = $length * 1.25;
-               // Generate random hex chars
-               $hex = MWCryptRand::generateHex( $length );
-               // Convert from base 16 to base 32 to get a proper password like string
-               return wfBaseConvert( $hex, 16, 32 );
+               return PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
        }
 
        /**
@@ -1046,15 +1058,9 @@ class User implements IDBAccessObject {
         * @param string|bool $name
         */
        public function loadDefaults( $name = false ) {
-
-               $passwordFactory = self::getPasswordFactory();
-
                $this->mId = 0;
                $this->mName = $name;
                $this->mRealName = '';
-               $this->mPassword = $passwordFactory->newFromCiphertext( null );
-               $this->mNewpassword = $passwordFactory->newFromCiphertext( null );
-               $this->mNewpassTime = null;
                $this->mEmail = '';
                $this->mOptionOverrides = null;
                $this->mOptionsLoaded = false;
@@ -1070,8 +1076,6 @@ class User implements IDBAccessObject {
                $this->mEmailAuthenticated = null;
                $this->mEmailToken = '';
                $this->mEmailTokenExpires = null;
-               $this->mPasswordExpires = null;
-               $this->resetPasswordExpiration( false );
                $this->mRegistration = wfTimestamp( TS_MW );
                $this->mGroups = array();
 
@@ -1243,7 +1247,6 @@ class User implements IDBAccessObject {
         */
        protected function loadFromRow( $row, $data = null ) {
                $all = true;
-               $passwordFactory = self::getPasswordFactory();
 
                $this->mGroups = null; // deferred
 
@@ -1280,31 +1283,6 @@ class User implements IDBAccessObject {
                        $all = false;
                }
 
-               if ( isset( $row->user_password ) ) {
-                       // Check for *really* old password hashes that don't even have a type
-                       // The old hash format was just an md5 hex hash, with no type information
-                       if ( preg_match( '/^[0-9a-f]{32}$/', $row->user_password ) ) {
-                               $row->user_password = ":A:{$this->mId}:{$row->user_password}";
-                       }
-
-                       try {
-                               $this->mPassword = $passwordFactory->newFromCiphertext( $row->user_password );
-                       } catch ( PasswordError $e ) {
-                               wfDebug( 'Invalid password hash found in database.' );
-                               $this->mPassword = $passwordFactory->newFromCiphertext( null );
-                       }
-
-                       try {
-                               $this->mNewpassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
-                       } catch ( PasswordError $e ) {
-                               wfDebug( 'Invalid password hash found in database.' );
-                               $this->mNewpassword = $passwordFactory->newFromCiphertext( null );
-                       }
-
-                       $this->mNewpassTime = wfTimestampOrNull( TS_MW, $row->user_newpass_time );
-                       $this->mPasswordExpires = wfTimestampOrNull( TS_MW, $row->user_password_expires );
-               }
-
                if ( isset( $row->user_email ) ) {
                        $this->mEmail = $row->user_email;
                        $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
@@ -1367,33 +1345,6 @@ class User implements IDBAccessObject {
                }
        }
 
-       /**
-        * Load the user's password hashes from the database
-        *
-        * This is usually called in a scenario where the actual User object was
-        * loaded from the cache, and then password comparison needs to be performed.
-        * Password hashes are not stored in memcached.
-        *
-        * @since 1.24
-        */
-       private function loadPasswords() {
-               if ( $this->getId() !== 0 &&
-                       ( $this->mPassword === null || $this->mNewpassword === null )
-               ) {
-                       $db = ( $this->queryFlagsUsed & self::READ_LATEST )
-                               ? wfGetDB( DB_MASTER )
-                               : wfGetDB( DB_SLAVE );
-
-                       $this->loadFromRow( $db->selectRow(
-                               'user',
-                               array( 'user_password', 'user_newpassword',
-                                       'user_newpass_time', 'user_password_expires' ),
-                               array( 'user_id' => $this->getId() ),
-                               __METHOD__
-                       ) );
-               }
-       }
-
        /**
         * Add the user to the group if he/she meets given criteria.
         *
@@ -2383,23 +2334,21 @@ class User implements IDBAccessObject {
        }
 
        /**
+        * @deprecated Removed in 1.27.
         * @return Password
         * @since 1.24
         */
        public function getPassword() {
-               $this->loadPasswords();
-
-               return $this->mPassword;
+               throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
        }
 
        /**
+        * @deprecated Removed in 1.27.
         * @return Password
         * @since 1.24
         */
        public function getTemporaryPassword() {
-               $this->loadPasswords();
-
-               return $this->mNewpassword;
+               throw new BadMethodCallException( __METHOD__ . ' has been removed in 1.27' );
        }
 
        /**
@@ -2413,16 +2362,14 @@ class User implements IDBAccessObject {
         * wipes it, so the account cannot be logged in until
         * a new password is set, for instance via e-mail.
         *
+        * @deprecated since 1.27. AuthManager is coming.
         * @param string $str New password to set
         * @throws PasswordError On failure
-        *
         * @return bool
         */
        public function setPassword( $str ) {
                global $wgAuth;
 
-               $this->loadPasswords();
-
                if ( $str !== null ) {
                        if ( !$wgAuth->allowPasswordChange() ) {
                                throw new PasswordError( wfMessage( 'password-change-forbidden' )->text() );
@@ -2438,7 +2385,9 @@ class User implements IDBAccessObject {
                        throw new PasswordError( wfMessage( 'externaldberror' )->text() );
                }
 
-               $this->setInternalPassword( $str );
+               $this->setToken();
+               $this->setOption( 'watchlisttoken', false );
+               $this->setPasswordInternal( $str );
 
                return true;
        }
@@ -2446,19 +2395,49 @@ class User implements IDBAccessObject {
        /**
         * Set the password and reset the random token unconditionally.
         *
+        * @deprecated since 1.27. AuthManager is coming.
         * @param string|null $str New password to set or null to set an invalid
         *  password hash meaning that the user will not be able to log in
         *  through the web interface.
         */
        public function setInternalPassword( $str ) {
-               $this->setToken();
-               $this->setOption( 'watchlisttoken', false );
+               global $wgAuth;
 
-               $passwordFactory = self::getPasswordFactory();
-               $this->mPassword = $passwordFactory->newFromPlaintext( $str );
+               if ( $wgAuth->allowSetLocalPassword() ) {
+                       $this->setToken();
+                       $this->setOption( 'watchlisttoken', false );
+                       $this->setPasswordInternal( $str );
+               }
+       }
 
-               $this->mNewpassword = $passwordFactory->newFromCiphertext( null );
-               $this->mNewpassTime = null;
+       /**
+        * Actually set the password and such
+        * @param string|null $str New password to set or null to set an invalid
+        *  password hash meaning that the user will not be able to log in
+        *  through the web interface.
+        */
+       private function setPasswordInternal( $str ) {
+               $id = self::idFromName( $this->getName() );
+               if ( $id ) {
+                       $passwordFactory = new PasswordFactory();
+                       $passwordFactory->init( RequestContext::getMain()->getConfig() );
+                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw->update(
+                               'user',
+                               array(
+                                       'user_password' => $passwordFactory->newFromPlaintext( $str )->toString(),
+                                       'user_newpassword' => PasswordFactory::newInvalidPassword()->toString(),
+                                       'user_newpass_time' => $dbw->timestampOrNull( null ),
+                               ),
+                               array(
+                                       'user_id' => $id,
+                               ),
+                               __METHOD__
+                       );
+                       $this->mPassword = null;
+               } else {
+                       $this->mPassword = $str;
+               }
        }
 
        /**
@@ -2493,19 +2472,27 @@ class User implements IDBAccessObject {
        /**
         * Set the password for a password reminder or new account email
         *
+        * @deprecated since 1.27, AuthManager is coming
         * @param string $str New password to set or null to set an invalid
         *  password hash meaning that the user will not be able to use it
         * @param bool $throttle If true, reset the throttle timestamp to the present
         */
        public function setNewpassword( $str, $throttle = true ) {
-               $this->loadPasswords();
+               $dbw = wfGetDB( DB_MASTER );
+
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               $update = array(
+                       'user_newpassword' => $passwordFactory->newFromPlaintext( $str )->toString(),
+               );
 
-               $this->mNewpassword = self::getPasswordFactory()->newFromPlaintext( $str );
                if ( $str === null ) {
-                       $this->mNewpassTime = null;
+                       $update['user_newpass_time'] = null;
                } elseif ( $throttle ) {
-                       $this->mNewpassTime = wfTimestampNow();
+                       $update['user_newpass_time'] = $dbw->timestamp();
                }
+
+               $dbw->update( 'user', $update, array( 'user_id' => $id ), __METHOD__ );
        }
 
        /**
@@ -2515,11 +2502,27 @@ class User implements IDBAccessObject {
         */
        public function isPasswordReminderThrottled() {
                global $wgPasswordReminderResendTime;
+
+               if ( !$wgPasswordReminderResendTime ) {
+                       return false;
+               }
+
                $this->load();
-               if ( !$this->mNewpassTime || !$wgPasswordReminderResendTime ) {
+
+               $db = ( $this->queryFlagsUsed & self::READ_LATEST )
+                       ? wfGetDB( DB_MASTER )
+                       : wfGetDB( DB_SLAVE );
+               $newpassTime = $db->selectField(
+                       'user',
+                       'user_newpass_time',
+                       array( 'user_id' => $this->getId() ),
+                       __METHOD__
+               );
+
+               if ( $newpassTime === null ) {
                        return false;
                }
-               $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgPasswordReminderResendTime * 3600;
+               $expiry = wfTimestamp( TS_UNIX, $newpassTime ) + $wgPasswordReminderResendTime * 3600;
                return time() < $expiry;
        }
 
@@ -3657,8 +3660,6 @@ class User implements IDBAccessObject {
         * @todo Only rarely do all these fields need to be set!
         */
        public function saveSettings() {
-               global $wgAuth;
-
                if ( wfReadOnly() ) {
                        // @TODO: caller should deal with this instead!
                        // This should really just be an exception.
@@ -3670,7 +3671,6 @@ class User implements IDBAccessObject {
                }
 
                $this->load();
-               $this->loadPasswords();
                if ( 0 == $this->mId ) {
                        return; // anon
                }
@@ -3681,17 +3681,10 @@ class User implements IDBAccessObject {
                $oldTouched = $this->mTouched;
                $newTouched = $this->newTouchedTimestamp();
 
-               if ( !$wgAuth->allowSetLocalPassword() ) {
-                       $this->mPassword = self::getPasswordFactory()->newFromCiphertext( null );
-               }
-
                $dbw = wfGetDB( DB_MASTER );
                $dbw->update( 'user',
                        array( /* SET */
                                'user_name' => $this->mName,
-                               'user_password' => $this->mPassword->toString(),
-                               'user_newpassword' => $this->mNewpassword->toString(),
-                               'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
                                'user_real_name' => $this->mRealName,
                                'user_email' => $this->mEmail,
                                'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
@@ -3699,7 +3692,6 @@ class User implements IDBAccessObject {
                                'user_token' => strval( $this->mToken ),
                                'user_email_token' => $this->mEmailToken,
                                'user_email_token_expires' => $dbw->timestampOrNull( $this->mEmailTokenExpires ),
-                               'user_password_expires' => $dbw->timestampOrNull( $this->mPasswordExpires ),
                        ), array( /* WHERE */
                                'user_id' => $this->mId,
                                'user_touched' => $dbw->timestamp( $oldTouched ) // CAS check
@@ -3757,10 +3749,6 @@ class User implements IDBAccessObject {
         * @param string $name Username to add
         * @param array $params Array of Strings Non-default parameters to save to
         *   the database as user_* fields:
-        *   - password: The user's password hash. Password logins will be disabled
-        *     if this is omitted.
-        *   - newpassword: Hash for a temporary password that has been mailed to
-        *     the user.
         *   - email: The user's email address.
         *   - email_authenticated: The email authentication timestamp.
         *   - real_name: The user's real name.
@@ -3771,9 +3759,15 @@ class User implements IDBAccessObject {
         * @return User|null User object, or null if the username already exists.
         */
        public static function createNew( $name, $params = array() ) {
+               foreach ( array( 'password', 'newpassword', 'newpass_time', 'password_expires' ) as $field ) {
+                       if ( isset( $params[$field] ) ) {
+                               wfDeprecated( __METHOD__ . " with param '$field'", '1.27' );
+                               unset( $params[$field] );
+                       }
+               }
+
                $user = new User;
                $user->load();
-               $user->loadPasswords();
                $user->setToken(); // init token
                if ( isset( $params['options'] ) ) {
                        $user->mOptions = $params['options'] + (array)$user->mOptions;
@@ -3782,12 +3776,13 @@ class User implements IDBAccessObject {
                $dbw = wfGetDB( DB_MASTER );
                $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
 
+               $noPass = PasswordFactory::newInvalidPassword()->toString();
+
                $fields = array(
                        'user_id' => $seqVal,
                        'user_name' => $name,
-                       'user_password' => $user->mPassword->toString(),
-                       'user_newpassword' => $user->mNewpassword->toString(),
-                       'user_newpass_time' => $dbw->timestampOrNull( $user->mNewpassTime ),
+                       'user_password' => $noPass,
+                       'user_newpassword' => $noPass,
                        'user_email' => $user->mEmail,
                        'user_email_authenticated' => $dbw->timestampOrNull( $user->mEmailAuthenticated ),
                        'user_real_name' => $user->mRealName,
@@ -3836,13 +3831,14 @@ class User implements IDBAccessObject {
         */
        public function addToDatabase() {
                $this->load();
-               $this->loadPasswords();
                if ( !$this->mToken ) {
                        $this->setToken(); // init token
                }
 
                $this->mTouched = $this->newTouchedTimestamp();
 
+               $noPass = PasswordFactory::newInvalidPassword()->toString();
+
                $dbw = wfGetDB( DB_MASTER );
                $inWrite = $dbw->writesOrCallbacksPending();
                $seqVal = $dbw->nextSequenceValue( 'user_user_id_seq' );
@@ -3850,9 +3846,8 @@ class User implements IDBAccessObject {
                        array(
                                'user_id' => $seqVal,
                                'user_name' => $this->mName,
-                               'user_password' => $this->mPassword->toString(),
-                               'user_newpassword' => $this->mNewpassword->toString(),
-                               'user_newpass_time' => $dbw->timestampOrNull( $this->mNewpassTime ),
+                               'user_password' => $noPass,
+                               'user_newpassword' => $noPass,
                                'user_email' => $this->mEmail,
                                'user_email_authenticated' => $dbw->timestampOrNull( $this->mEmailAuthenticated ),
                                'user_real_name' => $this->mRealName,
@@ -3894,6 +3889,11 @@ class User implements IDBAccessObject {
                }
                $this->mId = $dbw->insertId();
 
+               // Set the password now that it's in the DB, if applicable
+               if ( $this->mPassword !== null ) {
+                       $this->setPasswordInternal( $this->mPassword );
+               }
+
                // Clear instance cache other than user table data, which is already accurate
                $this->clearInstanceCache();
 
@@ -4002,13 +4002,14 @@ class User implements IDBAccessObject {
 
        /**
         * Check to see if the given clear-text password is one of the accepted passwords
+        * @deprecated since 1.27. AuthManager is coming.
         * @param string $password User password
         * @return bool True if the given password is correct, otherwise False
         */
        public function checkPassword( $password ) {
                global $wgAuth, $wgLegacyEncoding;
 
-               $this->loadPasswords();
+               $this->load();
 
                // Some passwords will give a fatal Status, which means there is
                // some sort of technical or security reason for this password to
@@ -4030,12 +4031,27 @@ class User implements IDBAccessObject {
                        return false;
                }
 
-               if ( !$this->mPassword->equals( $password ) ) {
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               $db = ( $this->queryFlagsUsed & self::READ_LATEST )
+                       ? wfGetDB( DB_MASTER )
+                       : wfGetDB( DB_SLAVE );
+
+               try {
+                       $mPassword = $passwordFactory->newFromCiphertext( $db->selectField(
+                               'user', 'user_password', array( 'user_id' => $this->getId() ), __METHOD__
+                       ) );
+               } catch ( PasswordError $e ) {
+                       wfDebug( 'Invalid password hash found in database.' );
+                       $mPassword = PasswordFactory::newInvalidPassword();
+               }
+
+               if ( !$mPassword->equals( $password ) ) {
                        if ( $wgLegacyEncoding ) {
                                // Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
                                // Check for this with iconv
                                $cp1252Password = iconv( 'UTF-8', 'WINDOWS-1252//TRANSLIT', $password );
-                               if ( $cp1252Password === $password || !$this->mPassword->equals( $cp1252Password ) ) {
+                               if ( $cp1252Password === $password || !$mPassword->equals( $cp1252Password ) ) {
                                        return false;
                                }
                        } else {
@@ -4043,10 +4059,8 @@ class User implements IDBAccessObject {
                        }
                }
 
-               $passwordFactory = self::getPasswordFactory();
-               if ( $passwordFactory->needsUpdate( $this->mPassword ) && !wfReadOnly() ) {
-                       $this->mPassword = $passwordFactory->newFromPlaintext( $password );
-                       $this->saveSettings();
+               if ( $passwordFactory->needsUpdate( $mPassword ) && !wfReadOnly() ) {
+                       $this->setPasswordInternal( $password );
                }
 
                return true;
@@ -4056,20 +4070,39 @@ class User implements IDBAccessObject {
         * Check if the given clear-text password matches the temporary password
         * sent by e-mail for password reset operations.
         *
+        * @deprecated since 1.27. AuthManager is coming.
         * @param string $plaintext
-        *
         * @return bool True if matches, false otherwise
         */
        public function checkTemporaryPassword( $plaintext ) {
                global $wgNewPasswordExpiry;
 
                $this->load();
-               $this->loadPasswords();
-               if ( $this->mNewpassword->equals( $plaintext ) ) {
-                       if ( is_null( $this->mNewpassTime ) ) {
+
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               $db = ( $this->queryFlagsUsed & self::READ_LATEST )
+                       ? wfGetDB( DB_MASTER )
+                       : wfGetDB( DB_SLAVE );
+
+               $row = $db->selectRow(
+                       'user',
+                       array( 'user_newpassword', 'user_newpass_time' ),
+                       array( 'user_id' => $this->getId() ),
+                       __METHOD__
+               );
+               try {
+                       $mNewpassword = $passwordFactory->newFromCiphertext( $row->user_newpassword );
+               } catch ( PasswordError $e ) {
+                       wfDebug( 'Invalid password hash found in database.' );
+                       $mNewpassword = PasswordFactory::newInvalidPassword();
+               }
+
+               if ( $mNewpassword->equals( $plaintext ) ) {
+                       if ( is_null( $row->user_newpass_time ) ) {
                                return true;
                        }
-                       $expiry = wfTimestamp( TS_UNIX, $this->mNewpassTime ) + $wgNewPasswordExpiry;
+                       $expiry = wfTimestamp( TS_UNIX, $row->user_newpass_time ) + $wgNewPasswordExpiry;
                        return ( time() < $expiry );
                } else {
                        return false;
@@ -4927,7 +4960,9 @@ class User implements IDBAccessObject {
         */
        public static function crypt( $password, $salt = false ) {
                wfDeprecated( __METHOD__, '1.24' );
-               $hash = self::getPasswordFactory()->newFromPlaintext( $password );
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               $hash = $passwordFactory->newFromPlaintext( $password );
                return $hash->toString();
        }
 
@@ -4956,7 +4991,9 @@ class User implements IDBAccessObject {
                        }
                }
 
-               $hash = self::getPasswordFactory()->newFromCiphertext( $hash );
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               $hash = $passwordFactory->newFromCiphertext( $hash );
                return $hash->equals( $password );
        }
 
@@ -5166,15 +5203,14 @@ class User implements IDBAccessObject {
        /**
         * Lazily instantiate and return a factory object for making passwords
         *
+        * @deprecated since 1.27, create a PasswordFactory directly instead
         * @return PasswordFactory
         */
        public static function getPasswordFactory() {
-               if ( self::$mPasswordFactory === null ) {
-                       self::$mPasswordFactory = new PasswordFactory();
-                       self::$mPasswordFactory->init( RequestContext::getMain()->getConfig() );
-               }
-
-               return self::$mPasswordFactory;
+               wfDeprecated( __METHOD__, '1.27' );
+               $ret = new PasswordFactory();
+               $ret->init( RequestContext::getMain()->getConfig() );
+               return $ret;
        }
 
        /**
@@ -5196,6 +5232,7 @@ class User implements IDBAccessObject {
         *
         * @todo FIXME: This does not belong here; put it in Html or Linker or somewhere
         *
+        * @deprecated since 1.27
         * @return array Array of HTML attributes suitable for feeding to
         *   Html::element(), directly or indirectly.  (Don't feed to Xml::*()!
         *   That will get confused by the boolean attribute syntax used.)
@@ -5209,7 +5246,7 @@ class User implements IDBAccessObject {
 
                # Note that the pattern requirement will always be satisfied if the
                # input is empty, so we need required in all cases.
-               #
+
                # @todo FIXME: Bug 23769: This needs to not claim the password is required
                # if e-mail confirmation is being used.  Since HTML5 input validation
                # is b0rked anyway in some browsers, just return nothing.  When it's
index 49d05d4..216d9b7 100644 (file)
@@ -119,7 +119,6 @@ class IEUrlExtension {
 
                if ( !preg_match( '/^[a-zA-Z0-9_-]+$/', $extension ) ) {
                        // Non-alphanumeric extension, unlikely to be registered.
-                       //
                        // The regex above is known to match all registered file extensions
                        // in a default Windows XP installation. It's important to allow
                        // extensions with ampersands and percent signs, since that reduces
index 86a3fef..e1f272b 100644 (file)
@@ -188,4 +188,38 @@ final class PasswordFactory {
                        return $password->needsUpdate();
                }
        }
+
+       /**
+        * Generate a random string suitable for a password
+        *
+        * @param int $minLength Minimum length of password to generate
+        * @return string
+        */
+       public static function generateRandomPasswordString( $minLength = 10 ) {
+               // Decide the final password length based on our min password length,
+               // stopping at a minimum of 10 chars.
+               $length = max( 10, $minLength );
+               // Multiply by 1.25 to get the number of hex characters we need
+               $length = $length * 1.25;
+               // Generate random hex chars
+               $hex = MWCryptRand::generateHex( $length );
+               // Convert from base 16 to base 32 to get a proper password like string
+               return wfBaseConvert( $hex, 16, 32 );
+       }
+
+       /**
+        * Create an InvalidPassword
+        *
+        * @return InvalidPassword
+        */
+       public static function newInvalidPassword() {
+               static $password = null;
+
+               if ( $password === null ) {
+                       $factory = new self();
+                       $password = new InvalidPassword( $factory, array( 'type' => '' ), null );
+               }
+
+               return $password;
+       }
 }
index 6a4347d..df68b12 100644 (file)
@@ -301,8 +301,8 @@ class SpecialChangePassword extends FormSpecialPage {
                        $remember = $this->getRequest()->getCookie( 'Token' ) !== null;
                        $user->setCookies( null, null, $remember );
                }
-               $user->resetPasswordExpiration();
                $user->saveSettings();
+               $this->resetPasswordExpiration( $user );
        }
 
        public function requiresUnblock() {
@@ -312,4 +312,28 @@ class SpecialChangePassword extends FormSpecialPage {
        protected function getGroupName() {
                return 'users';
        }
+
+       /**
+        * For resetting user password expiration, until AuthManager comes along
+        * @param User $user
+        */
+       private function resetPasswordExpiration( User $user ) {
+               global $wgPasswordExpirationDays;
+               $newExpire = null;
+               if ( $wgPasswordExpirationDays ) {
+                       $newExpire = wfTimestamp(
+                               TS_MW,
+                               time() + ( $wgPasswordExpirationDays * 24 * 3600 )
+                       );
+               }
+               // Give extensions a chance to force an expiration
+               Hooks::run( 'ResetPasswordExpiration', array( $this, &$newExpire ) );
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->update(
+                       'user',
+                       array( 'user_password_expires' => $dbw->timestampOrNull( $newExpire ) ),
+                       array( 'user_id' => $user->getID() ),
+                       __METHOD__
+               );
+       }
 }
index f1eb8c2..53e1d1b 100644 (file)
@@ -139,7 +139,7 @@ class SpecialPasswordReset extends FormSpecialPage {
         * @return bool|array
         */
        public function onSubmit( array $data ) {
-               global $wgAuth;
+               global $wgAuth, $wgMinimalPasswordLength;
 
                if ( isset( $data['Domain'] ) ) {
                        if ( $wgAuth->validDomain( $data['Domain'] ) ) {
@@ -254,7 +254,7 @@ class SpecialPasswordReset extends FormSpecialPage {
 
                $passwords = array();
                foreach ( $users as $user ) {
-                       $password = $user->randomPassword();
+                       $password = PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
                        $user->setNewpassword( $password );
                        $user->saveSettings();
                        $passwords[] = $this->msg( 'passwordreset-emailelement', $user->getName(), $password )
index 00ee327..447c3ef 100644 (file)
@@ -587,7 +587,7 @@ class PageArchive {
                $restored = 0;
 
                foreach ( $result as $row ) {
-                       // Check for key dupes due to shitty archive integrity.
+                       // Check for key dupes due to needed archive integrity.
                        if ( $row->ar_rev_id ) {
                                $exists = $dbw->selectField( 'revision', '1',
                                        array( 'rev_id' => $row->ar_rev_id ), __METHOD__ );
index 085cfee..0da598b 100644 (file)
@@ -826,7 +826,7 @@ class LoginForm extends SpecialPage {
                } elseif ( $wgBlockDisablesLogin && $u->isBlocked() ) {
                        // If we've enabled it, make it so that a blocked user cannot login
                        $retval = self::USER_BLOCKED;
-               } elseif ( $u->getPasswordExpired() == 'hard' ) {
+               } elseif ( $this->checkUserPasswordExpired( $u ) == 'hard' ) {
                        // Force reset now, without logging in
                        $retval = self::RESET_PASS;
                        $this->mAbortLoginErrorMsg = 'resetpass-expired';
@@ -994,7 +994,7 @@ class LoginForm extends SpecialPage {
                                        $this->getContext()->setLanguage( $userLang );
                                        // Reset SessionID on Successful login (bug 40995)
                                        $this->renewSessionId();
-                                       if ( $this->getUser()->getPasswordExpired() == 'soft' ) {
+                                       if ( $this->checkUserPasswordExpired( $this->getUser() ) == 'soft' ) {
                                                $this->resetLoginForm( $this->msg( 'resetpass-expired-soft' ) );
                                        } elseif ( $wgInvalidPasswordReset
                                                && !$user->isValidPassword( $this->mPassword )
@@ -1121,7 +1121,7 @@ class LoginForm extends SpecialPage {
        function mailPasswordInternal( $u, $throttle = true, $emailTitle = 'passwordremindertitle',
                $emailText = 'passwordremindertext'
        ) {
-               global $wgNewPasswordExpiry;
+               global $wgNewPasswordExpiry, $wgMinimalPasswordLength;
 
                if ( $u->getEmail() == '' ) {
                        return Status::newFatal( 'noemail', $u->getName() );
@@ -1134,7 +1134,7 @@ class LoginForm extends SpecialPage {
                $currentUser = $this->getUser();
                Hooks::run( 'User::mailPasswordInternal', array( &$currentUser, &$ip, &$u ) );
 
-               $np = $u->randomPassword();
+               $np = PasswordFactory::generateRandomPasswordString( $wgMinimalPasswordLength );
                $u->setNewpassword( $np, $throttle );
                $u->saveSettings();
                $userLanguage = $u->getOption( 'language' );
@@ -1717,4 +1717,25 @@ class LoginForm extends SpecialPage {
        protected function getGroupName() {
                return 'login';
        }
+
+       /**
+        * Private function to check password expiration, until AuthManager comes
+        * along to handle that.
+        * @param User $user
+        * @return string|bool
+        */
+       private function checkUserPasswordExpired( User $user ) {
+               global $wgPasswordExpireGrace;
+               $dbr = wfGetDB( DB_SLAVE );
+               $ts = $dbr->selectField( 'user', 'user_password_expires', array( 'user_id' => $user->getId() ) );
+
+               $expired = false;
+               $now = wfTimestamp();
+               $expUnix = wfTimestamp( TS_UNIX, $ts );
+               if ( $ts !== null && $expUnix < $now ) {
+                       $expired = ( $expUnix + $wgPasswordExpireGrace < $now ) ? 'hard' : 'soft';
+               }
+               return $expired;
+       }
+
 }
index 8514187..5f4a16a 100644 (file)
@@ -1763,7 +1763,7 @@ abstract class UploadBase {
                        // We're not using the normalized form of the extension.
                        // Normal form is lowercase, using most common of alternate
                        // extensions (eg 'jpg' rather than 'JPEG').
-                       //
+
                        // Check for another file using the normalized form...
                        $nt_lc = Title::makeTitle( NS_FILE, "{$partname}.{$normalizedExtension}" );
                        $file_lc = wfLocalFile( $nt_lc );
index b9a1bbf..b971c00 100644 (file)
@@ -221,7 +221,6 @@ class UploadStash {
                // If no key was supplied, make one.  a mysql insertid would be totally
                // reasonable here, except that for historical reasons, the key is this
                // random thing instead.  At least it's not guessable.
-               //
                // Some things that when combined will make a suitably unique key.
                // see: http://www.jwz.org/doc/mid.html
                list( $usec, $sec ) = explode( ' ', microtime() );
index b7767eb..7cedcc5 100644 (file)
@@ -68,10 +68,10 @@ class LanguageHy extends Language {
                                case 'accusative': # Հայցական հոլով
                                        # stub
                                        break;
-                               case 'instrumental':  #
+                               case 'instrumental':
                                        # stub
                                        break;
-                               case 'prepositional': #
+                               case 'prepositional':
                                        # stub
                                        break;
                        }
index 98d138d..3520298 100644 (file)
@@ -50,7 +50,6 @@ class LanguageWa extends Language {
                $datePreference = $this->dateFormat( $format );
 
                # ISO (YYYY-mm-dd) format
-               #
                # we also output this format for YMD (eg: 2001 January 15)
                if ( $datePreference == 'ISO 8601' ) {
                        $d = substr( $ts, 0, 4 ) . '-' . substr( $ts, 4, 2 ) . '-' . substr( $ts, 6, 2 );
@@ -64,7 +63,6 @@ class LanguageWa extends Language {
                }
 
                # Walloon format
-               #
                # we output this in all other cases
                $m = substr( $ts, 4, 2 );
                $n = substr( $ts, 6, 2 );
index b9301ac..3845370 100644 (file)
        "upload-dialog-button-done": "Done",
        "upload-dialog-button-save": "Save",
        "upload-dialog-button-upload": "Upload",
-       "upload-process-error": "An error occurred",
-       "upload-process-warning": "A warning occurred",
        "upload-form-label-select-file": "Select file",
        "upload-form-label-infoform-title": "Details",
        "upload-form-label-infoform-name": "Name",
index 6c3609a..6b593ce 100644 (file)
        "upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
        "upload-dialog-button-save": "Button to save the file\n{{Identical|Save}}",
        "upload-dialog-button-upload": "Button to initiate upload\n{{Identical|Upload}}",
-       "upload-process-error": "Error message from upload",
-       "upload-process-warning": "Warning message from upload",
        "upload-form-label-select-file": "Label for the select file widget\n{{Identical|Select file}}",
        "upload-form-label-infoform-title": "Title for the information form\n{{Identical|Detail}}",
        "upload-form-label-infoform-name": "Label for the file name input\n{{Identical|Name}}",
index 11a0590..0ed584c 100644 (file)
@@ -699,7 +699,6 @@ class TextPassDumper extends BackupDumper {
                $this->progress( "Spawning database subprocess: $cmd" );
                $this->spawnProc = proc_open( $cmd, $spec, $pipes );
                if ( !$this->spawnProc ) {
-                       // shit
                        $this->progress( "Subprocess spawn failed." );
 
                        return false;
index 6234db4..e0a0f49 100644 (file)
@@ -53,7 +53,7 @@ class CapsCleanup extends TableCleanup {
                        $this->error( "\$wgCapitalLinks is on -- no need for caps links cleanup.", true );
                }
 
-               $this->user = User::newFromName( 'Conversion script' );
+               $this->user = User::newSystemUser( 'Conversion script', array( 'steal' => true ) );
 
                $this->namespace = intval( $this->getOption( 'namespace', 0 ) );
                $this->dryrun = $this->hasOption( 'dry-run' );
index f4a5147..44d5810 100644 (file)
@@ -45,7 +45,7 @@ class CleanupSpam extends Maintenance {
                global $IP, $wgLocalDatabases, $wgUser;
 
                $username = wfMessage( 'spambot_username' )->text();
-               $wgUser = User::newFromName( $username );
+               $wgUser = User::newSystemUser( $username );
                if ( !$wgUser ) {
                        $this->error( "Invalid username specified in 'spambot_username' message: $username", true );
                }
index f6259e9..84e3aee 100644 (file)
@@ -49,11 +49,12 @@ class TableCleanup extends Maintenance {
 
        public function execute() {
                global $wgUser;
-               $wgUser = User::newFromName( 'Conversion script' );
                $this->dryrun = $this->hasOption( 'dry-run' );
                if ( $this->dryrun ) {
+                       $wgUser = User::newFromName( 'Conversion script' );
                        $this->output( "Checking for bad titles...\n" );
                } else {
+                       $wgUser = User::newSystemUser( 'Conversion script', array( 'steal' => true ) );
                        $this->output( "Checking and fixing bad titles...\n" );
                }
                $this->runTable( $this->defaultParams );
index e6321e1..a3b1561 100644 (file)
@@ -55,11 +55,15 @@ class DeleteBatch extends Maintenance {
                chdir( $oldCwd );
 
                # Options processing
-               $username = $this->getOption( 'u', 'Delete page script' );
+               $username = $this->getOption( 'u', false );
                $reason = $this->getOption( 'r', '' );
                $interval = $this->getOption( 'i', 0 );
 
-               $user = User::newFromName( $username );
+               if ( $username === false ) {
+                       $user = User::newSystemUser( 'Delete page script', array( 'steal' => true ) );
+               } else {
+                       $user = User::newFromName( $username );
+               }
                if ( !$user ) {
                        $this->error( "Invalid username", true );
                }
index 478e0d7..de5c5e2 100644 (file)
@@ -162,7 +162,7 @@ class DeleteEqualMessages extends Maintenance {
                        return;
                }
 
-               $user = User::newFromName( 'MediaWiki default' );
+               $user = User::newSystemUser( 'MediaWiki default', array( 'steal' => true ) );
                if ( !$user ) {
                        $this->error( "Invalid username", true );
                }
index 75ec12b..b67a957 100644 (file)
@@ -46,14 +46,18 @@ class EditCLI extends Maintenance {
        public function execute() {
                global $wgUser;
 
-               $userName = $this->getOption( 'user', 'Maintenance script' );
+               $userName = $this->getOption( 'user', false );
                $summary = $this->getOption( 'summary', '' );
                $minor = $this->hasOption( 'minor' );
                $bot = $this->hasOption( 'bot' );
                $autoSummary = $this->hasOption( 'autosummary' );
                $noRC = $this->hasOption( 'no-rc' );
 
-               $wgUser = User::newFromName( $userName );
+               if ( $userName === false ) {
+                       $wgUser = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
+               } else {
+                       $wgUser = User::newFromName( $userName );
+               }
                if ( !$wgUser ) {
                        $this->error( "Invalid username", true );
                }
index ad385e5..a040248 100644 (file)
@@ -70,9 +70,9 @@ $files = findFiles( $dir, $extensions, isset( $options['search-recursively'] ) )
 # Initialise the user for this operation
 $user = isset( $options['user'] )
        ? User::newFromName( $options['user'] )
-       : User::newFromName( 'Maintenance script' );
+       : User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
 if ( !$user instanceof User ) {
-       $user = User::newFromName( 'Maintenance script' );
+       $user = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
 }
 $wgUser = $user;
 
index 6566a60..5dfd2a8 100644 (file)
@@ -41,7 +41,12 @@ class ImportSiteScripts extends Maintenance {
        public function execute() {
                global $wgUser;
 
-               $user = User::newFromName( $this->getOption( 'username', 'ScriptImporter' ) );
+               $username = $this->getOption( 'username', false );
+               if ( $username === false ) {
+                       $user = User::newSystemUser( 'ScriptImporter', array( 'steal' => true ) );
+               } else {
+                       $user = User::newFromName( $username );
+               }
                $wgUser = $user;
 
                $baseUrl = $this->getArg( 1 );
index a27a772..5849908 100644 (file)
@@ -61,7 +61,7 @@ class MoveBatch extends Maintenance {
                chdir( $oldCwd );
 
                # Options processing
-               $user = $this->getOption( 'u', 'Move page script' );
+               $user = $this->getOption( 'u', false );
                $reason = $this->getOption( 'r', '' );
                $interval = $this->getOption( 'i', 0 );
                $noredirects = $this->getOption( 'noredirects', false );
@@ -75,7 +75,11 @@ class MoveBatch extends Maintenance {
                if ( !$file ) {
                        $this->error( "Unable to read file, exiting", true );
                }
-               $wgUser = User::newFromName( $user );
+               if ( $user === false ) {
+                       $wgUser = User::newSystemUser( 'Move page script', array( 'steal' => true ) );
+               } else {
+                       $wgUser = User::newFromName( $user );
+               }
                if ( !$wgUser ) {
                        $this->error( "Invalid username", true );
                }
index ec03f93..449a7ad 100644 (file)
@@ -41,7 +41,7 @@ class Protect extends Maintenance {
        }
 
        public function execute() {
-               $userName = $this->getOption( 'u', 'Maintenance script' );
+               $userName = $this->getOption( 'u', false );
                $reason = $this->getOption( 'r', '' );
 
                $cascade = $this->hasOption( 'cascade' );
@@ -53,7 +53,11 @@ class Protect extends Maintenance {
                        $protection = "";
                }
 
-               $user = User::newFromName( $userName );
+               if ( $userName === false ) {
+                       $user = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
+               } else {
+                       $user = User::newFromName( $userName );
+               }
                if ( !$user ) {
                        $this->error( "Invalid username", true );
                }
index 967dda8..7be5a1f 100644 (file)
@@ -76,7 +76,7 @@ class RollbackEdits extends Maintenance {
                        return;
                }
 
-               $doer = User::newFromName( 'Maintenance script' );
+               $doer = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
 
                foreach ( $titles as $t ) {
                        $page = WikiPage::factory( $t );
index 4b58b60..638b99d 100644 (file)
@@ -130,8 +130,7 @@ CREATE TABLE /*_*/user (
   --
   user_editcount int,
 
-  -- Expiration date for user password. Use $user->expirePassword()
-  -- to force a password reset.
+  -- Expiration date for user password.
   user_password_expires varbinary(14) DEFAULT NULL
 
 ) /*$wgDBTableOptions*/;
index adebd27..71b4de1 100644 (file)
@@ -35,7 +35,7 @@ class Undelete extends Maintenance {
        public function execute() {
                global $wgUser;
 
-               $user = $this->getOption( 'user', 'Command line script' );
+               $user = $this->getOption( 'user', false );
                $reason = $this->getOption( 'reason', '' );
                $pageName = $this->getArg();
 
@@ -43,7 +43,11 @@ class Undelete extends Maintenance {
                if ( !$title ) {
                        $this->error( "Invalid title", true );
                }
-               $wgUser = User::newFromName( $user );
+               if ( $user === false ) {
+                       $wgUser = User::newSystemUser( 'Command line script', array( 'steal' => true ) );
+               } else {
+                       $wgUser = User::newFromName( $user );
+               }
                if ( !$wgUser ) {
                        $this->error( "Invalid username", true );
                }
index 0ec4b6b..fee1e7c 100644 (file)
@@ -1175,8 +1175,6 @@ return array(
                        'mediawiki.jqueryMsg',
                ),
                'messages' => array(
-                       'upload-process-error',
-                       'upload-process-warning',
                        'upload-form-label-select-file',
                        'upload-form-label-infoform-title',
                        'upload-form-label-infoform-name',
@@ -1850,8 +1848,6 @@ return array(
        'mediawiki.legacy.commonPrint' => array(
                'position' => 'top',
                'styles' => array(
-                       // @todo: Remove mediawiki.page.gallery when cache has cleared
-                       'resources/src/mediawiki/page/gallery-print.css' => array( 'media' => 'print' ),
                        'resources/src/mediawiki.legacy/commonPrint.css' => array( 'media' => 'print' )
                ),
        ),
@@ -1864,8 +1860,6 @@ return array(
        'mediawiki.legacy.shared' => array(
                'position' => 'top',
                'styles' => array(
-                       // @todo: Remove when mediawiki.page.gallery in cached html.
-                       'resources/src/mediawiki/page/gallery.css',
                        'resources/src/mediawiki.legacy/shared.css' => array( 'media' => 'screen' )
                ),
        ),
index 4efb02a..d3fad8d 100644 (file)
@@ -22,6 +22,8 @@
        "ooui-dialog-process-dismiss": "Lukk",
        "ooui-dialog-process-retry": "Prøv igjen",
        "ooui-dialog-process-continue": "Fortsett",
+       "ooui-selectfile-button-select": "Velg en fil",
        "ooui-selectfile-not-supported": "Filvalg er ikke støttet",
-       "ooui-selectfile-placeholder": "Ingen fil er valgt"
+       "ooui-selectfile-placeholder": "Ingen fil er valgt",
+       "ooui-selectfile-dragdrop-placeholder": "Slipp fil her"
 }
index 0661b3f..a69d76f 100644 (file)
@@ -10,6 +10,7 @@
        },
        "ooui-outline-control-move-down": "ਨੀਚੇ ਲੈਕੇ ਜਾਓ",
        "ooui-outline-control-move-up": "ਉੱਤੇ ਲੈਕੇ ਜਾਓ",
+       "ooui-outline-control-remove": "ਆਈਟਮ ਹਟਾਓ",
        "ooui-toolbar-more": "ਹੋਰ",
        "ooui-toolgroup-expand": "ਹੋਰ",
        "ooui-toolgroup-collapse": "ਥੋੜ੍ਹੇ",
@@ -18,5 +19,9 @@
        "ooui-dialog-process-error": "ਕੁਝ ਗਲਤ ਹੋ ਗਿਆ",
        "ooui-dialog-process-dismiss": "ਰੱਦ ਕਰੋ",
        "ooui-dialog-process-retry": "ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ",
-       "ooui-dialog-process-continue": "ਜਾਰੀ ਰੱਖੋ"
+       "ooui-dialog-process-continue": "ਜਾਰੀ ਰੱਖੋ",
+       "ooui-selectfile-button-select": "ਫ਼ਾਈਲ ਚੁਣੋ",
+       "ooui-selectfile-not-supported": "ਚੁਣੀ ਗਈ ਫ਼ਾਈਲ ਖੋਲੀ ਨਹੀਂ ਜਾ ਸਕਦੀ",
+       "ooui-selectfile-placeholder": "ਕੋਈ ਫ਼ਾਈਲ ਚੁਣੀ ਨਹੀਂ ਗਈ",
+       "ooui-selectfile-dragdrop-placeholder": "ਫ਼ਾਈਲ ਇੱਥੇ ਸਿੱਟੋ"
 }
index 6fb7be6..5012934 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.11
+ * OOjs UI v0.12.12
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-07T20:48:23Z
+ * Date: 2015-10-13T20:38:26Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
        width: 100%;
 }
 .oo-ui-capsuleMultiSelectWidget-handle {
-       background: #ffffff;
+       background-color: #ffffff;
        cursor: text;
        min-height: 2.4em;
        margin-right: 0.5em;
index d0c8e51..dab3c78 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.11
+ * OOjs UI v0.12.12
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-07T20:48:15Z
+ * Date: 2015-10-13T20:38:18Z
  */
 /**
  * @class
index 08edbc6..a19ebea 100644 (file)
@@ -1,20 +1,12 @@
-/*
- * Local backports:
- *
- * - aadaa8a187d36ded603eebbdff47efe133908f6c
- *   CapsuleMultiSelectWidget: Set 'background-color' rather than 'background'
- *   Required for mw.widgets.CategorySelector's pending state to display.
- */
-
 /*!
- * OOjs UI v0.12.11
+ * OOjs UI v0.12.12
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-07T20:48:23Z
+ * Date: 2015-10-13T20:38:26Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
        min-width: 1em;
        border-radius: 2px;
        position: relative;
-       -webkit-transition: background 100ms ease, color 100ms ease, box-shadow 100ms ease;
-          -moz-transition: background 100ms ease, color 100ms ease, box-shadow 100ms ease;
-           -ms-transition: background 100ms ease, color 100ms ease, box-shadow 100ms ease;
-            -o-transition: background 100ms ease, color 100ms ease, box-shadow 100ms ease;
-               transition: background 100ms ease, color 100ms ease, box-shadow 100ms ease;
+       -webkit-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+          -moz-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+           -ms-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+            -o-transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
+               transition: background 100ms ease, color 100ms ease, border-color 100ms ease, box-shadow 100ms ease;
 }
 .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:hover,
 .oo-ui-buttonElement-framed > .oo-ui-buttonElement-button:focus {
        opacity: 0;
        z-index: 1;
        position: relative;
+       cursor: pointer;
        margin: 0;
        width: 1.6em;
        height: 1.6em;
        max-width: none;
 }
 .oo-ui-checkboxInputWidget input[type="checkbox"] + span {
-       cursor: pointer;
        -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
           -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
            -ms-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
        opacity: 0;
        z-index: 1;
        position: relative;
+       cursor: pointer;
        margin: 0;
        width: 1.6em;
        height: 1.6em;
        max-width: none;
 }
 .oo-ui-radioInputWidget input[type="radio"] + span {
-       cursor: pointer;
        -webkit-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
           -moz-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
            -ms-transition: background-size 200ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
        margin-right: 0;
 }
 .oo-ui-dropdownWidget-handle {
-       padding: 0.3em 0;
+       padding: 0.5em 0;
        height: 2.275em;
        border: 1px solid #cccccc;
        border-radius: 0.1em;
index 60acdd2..e6344f2 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.12.11
+ * OOjs UI v0.12.12
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-07T20:48:15Z
+ * Date: 2015-10-13T20:38:18Z
  */
 /**
  * @class
@@ -30,7 +30,7 @@ OO.inheritClass( OO.ui.MediaWikiTheme, OO.ui.Theme );
  */
 OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
        // Parent method
-       var variant,
+       var variant, isFramed, isActive,
                variants = {
                        warning: false,
                        invert: false,
@@ -39,13 +39,13 @@ OO.ui.MediaWikiTheme.prototype.getElementClasses = function ( element ) {
                        destructive: false
                },
                // Parent method
-               classes = OO.ui.MediaWikiTheme.parent.prototype.getElementClasses.call( this, element ),
-               isFramed;
+               classes = OO.ui.MediaWikiTheme.parent.prototype.getElementClasses.call( this, element );
 
        if ( element.supports( [ 'hasFlag' ] ) ) {
                isFramed = element.supports( [ 'isFramed' ] ) && element.isFramed();
+               isActive = element.supports( [ 'isActive' ] ) && element.isActive();
                if (
-                       ( isFramed && ( element.isDisabled() || element.hasFlag( 'primary' ) ) ) ||
+                       ( isFramed && ( isActive || element.isDisabled() || element.hasFlag( 'primary' ) ) ) ||
                        ( !isFramed && element.hasFlag( 'primary' ) )
                ) {
                        variants.invert = true;
index 951acc7..aeff69e 100644 (file)
@@ -1,20 +1,12 @@
-/*
- * Local backports:
- *
- * - 9aba218a882ff45b07410a3ce9d5cdfd8e567e26
- *   CapsuleMultiSelectWidget: When 'allowArbitrary' is true, don't require 'Enter' to confirm
- *   Required for more intuitive behavior of mw.widgets.CategorySelector.
- */
-
 /*!
- * OOjs UI v0.12.11
+ * OOjs UI v0.12.12
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
  * Copyright 2011–2015 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-10-07T20:48:15Z
+ * Date: 2015-10-13T20:38:18Z
  */
 ( function ( OO ) {
 
@@ -1113,7 +1105,8 @@ OO.ui.ActionSet.prototype.organize = function () {
  * @cfg {Array} [content] An array of content elements to append (after #text).
  *  Strings will be html-escaped; use an OO.ui.HtmlSnippet to append raw HTML.
  *  Instances of OO.ui.Element will have their $element appended.
- * @cfg {jQuery} [$content] Content elements to append (after #text)
+ * @cfg {jQuery} [$content] Content elements to append (after #text).
+ * @cfg {jQuery} [$element] Wrapper element. Defaults to a new element with #getTagName.
  * @cfg {Mixed} [data] Custom data of any type or combination of types (e.g., string, number, array, object).
  *  Data can also be specified with the #setData method.
  */
@@ -4241,7 +4234,7 @@ OO.initClass( OO.ui.Theme );
  * @param {OO.ui.Element} element Element for which to get classes
  * @return {Object.<string,string[]>} Categorized class names with `on` and `off` lists
  */
-OO.ui.Theme.prototype.getElementClasses = function ( /* element */ ) {
+OO.ui.Theme.prototype.getElementClasses = function () {
        return { on: [], off: [] };
 };
 
@@ -4633,20 +4626,30 @@ OO.ui.mixin.ButtonElement.prototype.toggleFramed = function ( framed ) {
 };
 
 /**
- * Set the button to its 'active' state.
+ * Set the button's active state.
  *
  * The active state occurs when a {@link OO.ui.ButtonOptionWidget ButtonOptionWidget} or
  * a {@link OO.ui.ToggleButtonWidget ToggleButtonWidget} is pressed. This method does nothing
  * for other button types.
  *
- * @param {boolean} [value] Make button active
+ * @param {boolean} value Make button active
  * @chainable
  */
 OO.ui.mixin.ButtonElement.prototype.setActive = function ( value ) {
-       this.$element.toggleClass( 'oo-ui-buttonElement-active', !!value );
+       this.active = !!value;
+       this.$element.toggleClass( 'oo-ui-buttonElement-active', this.active );
        return this;
 };
 
+/**
+ * Check if the button is active
+ *
+ * @return {boolean} The button is active
+ */
+OO.ui.mixin.ButtonElement.prototype.isActive = function () {
+       return this.active;
+};
+
 /**
  * Any OOjs UI widget that contains other widgets (such as {@link OO.ui.ButtonWidget buttons} or
  * {@link OO.ui.OptionWidget options}) mixes in GroupElement. Adding, removing, and clearing
@@ -14109,6 +14112,18 @@ OO.ui.SelectFileWidget.prototype.setValue = function ( file ) {
        }
 };
 
+/**
+ * Focus the widget.
+ *
+ * Focusses the select file button.
+ *
+ * @chainable
+ */
+OO.ui.SelectFileWidget.prototype.focus = function () {
+       this.selectButton.$button[ 0 ].focus();
+       return this;
+};
+
 /**
  * Update the user interface when a file is selected or unselected
  *
index c37c723..0e2546f 100644 (file)
         * @param {OO.ui.OptionWidget} item Chosen item
         */
        mw.widgets.TitleSearchWidget.prototype.onTitleSearchResultsChoose = function ( item ) {
-               // TOOD: Pressing enter can incorrectly trigger 'choose' with null.
-               // Remove this check when oojs-ui 0.12.10 lands.
-               if ( item ) {
-                       this.getQuery().setValue( item.getData() );
-               }
+               this.getQuery().setValue( item.getData() );
        };
 
        /**
index 9bbbf9f..8af1428 100644 (file)
@@ -181,7 +181,6 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                $fileName = $this->getNewTempFile();
 
                // Converting the temporary /file/ to a /directory/
-               //
                // The following is not atomic, but at least we now have a single place,
                // where temporary directory creation is bundled and can be improved
                unlink( $fileName );
@@ -493,13 +492,13 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
 
                if ( $user->idForName() == 0 ) {
                        $user->addToDatabase();
-                       $user->setPassword( 'UTSysopPassword' );
-
-                       $user->addGroup( 'sysop' );
-                       $user->addGroup( 'bureaucrat' );
-                       $user->saveSettings();
+                       TestUser::setPasswordForUser( $user, 'UTSysopPassword' );
                }
 
+               // Always set groups, because $this->resetDB() wipes them out
+               $user->addGroup( 'sysop' );
+               $user->addGroup( 'bureaucrat' );
+
                // Make 1 page with 1 revision
                $page = WikiPage::factory( Title::newFromText( 'UTPage' ) );
                if ( $page->getId() == 0 ) {
index 7b0de86..e69fa20 100644 (file)
@@ -26,7 +26,7 @@ class BlockTest extends MediaWikiLangTestCase {
                $user = User::newFromName( 'UTBlockee' );
                if ( $user->getID() == 0 ) {
                        $user->addToDatabase();
-                       $user->setPassword( 'UTBlockeePassword' );
+                       TestUser::setPasswordForUser( $user, 'UTBlockeePassword' );
 
                        $user->saveSettings();
                }
@@ -136,9 +136,9 @@ class BlockTest extends MediaWikiLangTestCase {
        public function testBlockedUserCanNotCreateAccount() {
                $username = 'BlockedUserToCreateAccountWith';
                $u = User::newFromName( $username );
-               $u->setPassword( 'NotRandomPass' );
                $u->setId( 14146 );
                $u->addToDatabase();
+               TestUser::setPasswordForUser( $u, 'NotRandomPass' );
                unset( $u );
 
                // Sanity check
@@ -374,8 +374,8 @@ class BlockTest extends MediaWikiLangTestCase {
                # Set up the target
                $u = User::newFromName( $username );
                if ( $u->getID() == 0 ) {
-                       $u->setPassword( 'TotallyObvious' );
                        $u->addToDatabase();
+                       TestUser::setPasswordForUser( $u, 'TotallyObvious' );
                }
                unset( $u );
 
index f011f47..9093564 100644 (file)
@@ -125,21 +125,16 @@ class LinkFilterTest extends MediaWikiLangTestCase {
                        array( '', 'gopher://*.test.com/', 'gopher://gopher.test.com/0/v2/vstat' ),
                        array( 'telnet://', '*.test.com', 'telnet://shell.test.com/~home/' ),
 
-                       //
                        // The following only work in PHP >= 5.3.7, due to a bug in parse_url which eats
                        // the path from the url (https://bugs.php.net/bug.php?id=54180)
-                       //
                        // array( '', 'http://test.com', 'http://test.com/index?arg=1' ),
                        // array( 'http://', '*.test.com', 'http://www.test.com/index?arg=1' ),
                        // array( '' ,
                        //    'http://xx23124:__ffdfdef__@www.test.com:12345/dir' ,
                        //    'http://name:pass@www.test.com:12345/dir/dir/file.xyz.php#__se__?arg1=_&arg2[]=4rtg'
                        // ),
-                       //
 
-                       //
                        // Tests for false positives
-                       //
                        array( 'http://', 'test.com', 'http://www.test.com', false ),
                        array( 'http://', 'www1.test.com', 'http://www.test.com', false ),
                        array( 'http://', '*.test.com', 'http://www.test.t.com', false ),
@@ -166,10 +161,8 @@ class LinkFilterTest extends MediaWikiLangTestCase {
                        array( '', 'http://test.com:8080/dir/', 'http://test.com:808/dir/', false ),
                        array( '', 'http://test.com/dir/index.html', 'http://test.com/dir/index.php', false ),
 
-                       //
                        // These are false positives too and ideally shouldn't match, but that
                        // would require using regexes and RLIKE instead of LIKE
-                       //
                        // array( null, 'http://*.test.com', 'http://www.test.com:80', false ),
                        // array( '', 'https://*.wikimedia.org/r/#/q/status:open,n,z',
                        //      'https://gerrit.wikimedia.org/XXX/r/#/q/status:open,n,z', false ),
index 96b2251..6887ed1 100644 (file)
@@ -65,8 +65,8 @@ class TestUser {
                }
 
                // Update the user to use the password and other details
-               $change = $this->setPassword( $this->password ) ||
-                       $this->setEmail( $email ) ||
+               $this->setPassword( $this->password );
+               $change = $this->setEmail( $email ) ||
                        $this->setRealName( $realname );
 
                // Adjust groups by adding any missing ones and removing any extras
@@ -110,26 +110,36 @@ class TestUser {
 
        /**
         * @param string $password
-        * @return bool
         */
        private function setPassword( $password ) {
-               $passwordFactory = $this->user->getPasswordFactory();
-               $oldDefaultType = $passwordFactory->getDefaultType();
-
-               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
-               $passwordFactory->setDefaultType( 'A' );
-               $newPassword = $passwordFactory->newFromPlaintext( $password, $this->user->getPassword() );
+               self::setPasswordForUser( $this->user, $password );
+       }
 
-               $change = false;
-               if ( !$this->user->getPassword()->equals( $newPassword ) ) {
-                       // Password changed
-                       $this->user->setPassword( $password );
-                       $change = true;
+       /**
+        * Set the password on a testing user
+        *
+        * This assumes we're still using the generic AuthManager config from
+        * PHPUnitMaintClass::finalSetup(), and just sets the password in the
+        * database directly.
+        * @param User $user
+        * @param string $password
+        */
+       public static function setPasswordForUser( User $user, $password ) {
+               if ( !$user->getId() ) {
+                       throw new MWException( "Passed User has not been added to the database yet!" );
                }
 
-               $passwordFactory->setDefaultType( $oldDefaultType );
-
-               return $change;
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
+               $passwordFactory->setDefaultType( 'A' );
+               $pwhash = $passwordFactory->newFromPlaintext( $password );
+               wfGetDB( DB_MASTER )->update(
+                       'user',
+                       array( 'user_password' => $pwhash->toString() ),
+                       array( 'user_id' => $user->getId() ),
+                       __METHOD__
+               );
        }
 
        /**
index 17c1b8e..4c6f083 100644 (file)
@@ -276,26 +276,6 @@ class UserTest extends MediaWikiTestCase {
                $this->assertEquals( 'test', $this->user->getOption( 'userjs-someoption' ) );
        }
 
-       /**
-        * Test password expiration.
-        * @covers User::getPasswordExpired()
-        */
-       public function testPasswordExpire() {
-               $this->setMwGlobals( 'wgPasswordExpireGrace', 3600 * 24 * 7 ); // 7 days
-
-               $user = User::newFromName( 'UnitTestUser' );
-               $user->loadDefaults( 'UnitTestUser' );
-               $this->assertEquals( false, $user->getPasswordExpired() );
-
-               $ts = time() - ( 3600 * 24 * 1 ); // 1 day ago
-               $user->expirePassword( $ts );
-               $this->assertEquals( 'soft', $user->getPasswordExpired() );
-
-               $ts = time() - ( 3600 * 24 * 10 ); // 10 days ago
-               $user->expirePassword( $ts );
-               $this->assertEquals( 'hard', $user->getPasswordExpired() );
-       }
-
        /**
         * Test password validity checks. There are 3 checks in core,
         *      - ensure the password meets the minimal length
index e0488b7..ffd2f5e 100644 (file)
@@ -22,7 +22,7 @@ class ApiBlockTest extends ApiTestCase {
 
                if ( $user->getId() == 0 ) {
                        $user->addToDatabase();
-                       $user->setPassword( 'UTApiBlockeePassword' );
+                       TestUser::setPasswordForUser( $user, 'UTApiBlockeePassword' );
 
                        $user->saveSettings();
                }
index 8d134f7..3945102 100644 (file)
@@ -29,7 +29,7 @@ class ApiCreateAccountTest extends ApiTestCase {
                        $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
                }
 
-               $password = User::randomPassword();
+               $password = PasswordFactory::generateRandomPasswordString();
 
                $ret = $this->doApiRequest( array(
                        'action' => 'createaccount',
index f8da0ff..d187fed 100644 (file)
@@ -15,7 +15,7 @@ class UserWrapper {
                                "email" => "test@example.com",
                                "real_name" => "Test User" ) );
                }
-               $this->user->setPassword( $this->password );
+               TestUser::setPasswordForUser( $this->user, $this->password );
 
                if ( $group !== '' ) {
                        $this->user->addGroup( $group );
index 6b22000..d931b39 100644 (file)
@@ -13,7 +13,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
                $user = User::newFromName( 'UTMale' );
                if ( $user->getID() == 0 ) {
                        $user->addToDatabase();
-                       $user->setPassword( 'UTMalePassword' );
+                       TestUser::setPasswordForUser( $user, 'UTMalePassword' );
                }
                // ensure the right gender
                $user->setOption( 'gender', 'male' );
@@ -22,7 +22,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
                $user = User::newFromName( 'UTFemale' );
                if ( $user->getID() == 0 ) {
                        $user->addToDatabase();
-                       $user->setPassword( 'UTFemalePassword' );
+                       TestUser::setPasswordForUser( $user, 'UTFemalePassword' );
                }
                // ensure the right gender
                $user->setOption( 'gender', 'female' );
@@ -31,7 +31,7 @@ class GenderCacheTest extends MediaWikiLangTestCase {
                $user = User::newFromName( 'UTDefaultGender' );
                if ( $user->getID() == 0 ) {
                        $user->addToDatabase();
-                       $user->setPassword( 'UTDefaultGenderPassword' );
+                       TestUser::setPasswordForUser( $user, 'UTDefaultGenderPassword' );
                }
                // ensure the default gender
                $user->setOption( 'gender', null );
index 5ad8aca..e8a4d5c 100644 (file)
@@ -25,14 +25,16 @@ class PasswordTest extends MediaWikiTestCase {
         * @covers InvalidPassword::equals
         */
        public function testInvalidUnequalInvalid() {
-               $invalid1 = User::getPasswordFactory()->newFromCiphertext( null );
-               $invalid2 = User::getPasswordFactory()->newFromCiphertext( null );
+               $passwordFactory = new PasswordFactory();
+               $invalid1 = $passwordFactory->newFromCiphertext( null );
+               $invalid2 = $passwordFactory->newFromCiphertext( null );
 
                $this->assertFalse( $invalid1->equals( $invalid2 ) );
        }
 
        public function testInvalidPlaintext() {
-               $invalid = User::getPasswordFactory()->newFromPlaintext( null );
+               $passwordFactory = new PasswordFactory();
+               $invalid = $passwordFactory->newFromPlaintext( null );
 
                $this->assertInstanceOf( 'InvalidPassword', $invalid );
        }
index 8c76397..c846c06 100644 (file)
@@ -102,7 +102,7 @@ abstract class DumpTestCase extends MediaWikiLangTestCase {
                // expectOutput[...] functions. However, the PHPUnit shipped prediactes
                // do not allow to check /each/ line of the output using /readable/ REs.
                // So we ...
-               //
+
                // 1. ... add a dummy output checking to make PHPUnit not complain
                //    about unchecked test output
                $this->expectOutputRegex( '//' );
index 454e9c1..5c6a6cd 100644 (file)
@@ -4,7 +4,6 @@
 // instead of the MaintenanceFixup hack below. However, we cannot do
 // without changing the visibility and without working around hacks in
 // Maintenance.php
-//
 // For the same reason, we cannot just use FakeMaintenance.
 
 /**
index c216864..f5dd98b 100644 (file)
@@ -301,7 +301,6 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
 
                // The dump (hopefully) did take long enough to produce more than one
                // checkpoint file.
-               //
                // We now check all the checkpoint files for validity.
 
                $files = scandir( $nameOutputDir );